#include <Shop.h>

#include <assert.h>
#include <QRegExp>
#include <QDir>
#include <QDebug>

#include "ProductsParser.h"
#include "CustomersParser.h"
#include "InvoicesModel.h"
#include "InvoicesParser.h"

using namespace ecocity;

//---------------------------------------------------------

Shop::Shop( const QString& file_products_csv,
            const QString& file_customers_csv,
            const QString& file_invoices_csv )
{
    std::vector< Product > products;
    std::vector< Customer > customers;
    std::vector< Proveider > proveiders;

    ecocity::ProductsParser::parse( file_products_csv, products, proveiders );
    ecocity::CustomerParser::parse( file_customers_csv, customers );

    m_products = products;
    m_customers = customers;
    m_proveiders = proveiders;

    // TODO Clean Shop (remove parse invoices from it)
    // and create InvoicesParser that fills the shop object
    ecocity::InvoicesParser::parse( file_invoices_csv, this );
    //parse_invoices( file_invoices_csv );
}

//---------------------------------------------------------

QStringList
Shop::customer_types( )
{
    QStringList list;

    list << Customer::to_string( Customer::CONSUMIDOR_FINAL )
         << Customer::to_string( Customer::COOPERATIVA )
         << Customer::to_string( Customer::TIENDA )
         << Customer::to_string( Customer::FRIENDS );

    return list;
}

//---------------------------------------------------------

void
Shop::add_customer( const Customer &customer )
{
    m_customers.push_back( customer );
}

//---------------------------------------------------------

void
Shop::add_product( const Product &product )
{
    m_products.push_back( product );
}

//---------------------------------------------------------

void
Shop::add_invoice( const Invoice &invoice )
{
    m_invoices.push_back( invoice );
}

//---------------------------------------------------------

void
Shop::set_products( const std::vector<Product> &products )
{
    m_products = products;
}

//---------------------------------------------------------

const std::vector< Product >&
Shop::products( ) const
{
    return m_products;
}

//---------------------------------------------------------

const std::vector< Customer >&
Shop::customers( ) const
{
    return m_customers;
}

//---------------------------------------------------------

const std::vector< Invoice >&
Shop::invoices( ) const
{
    return m_invoices;
}

//---------------------------------------------------------

const std::vector< Proveider >&
Shop::proveiders( ) const
{
    return m_proveiders;
}

//---------------------------------------------------------

Customer
Shop::customer( const QString& customer_name ) const
{
    for( uint i = 0; i < m_customers.size( ); ++i )
    {
        if ( m_customers[ i ].name( ) == customer_name )
        {
            return m_customers[ i ];
        }
    }

    return Customer( );
}

//---------------------------------------------------------

bool
Shop::remove_customer( const QString &customer_name,
                       const QString &address,
                       Customer::TYPE type )
{
    bool removed = false;

    for( uint i = 0; i < m_customers.size( ); ++i )
    {
        if ( m_customers[ i ].name( ) == customer_name
             && m_customers[ i ].address() == address
             && m_customers[ i ].type() == type )
        {
            m_customers.erase( m_customers.begin() + i );
            removed = true;
            break;
        }
    }

    return removed;
}

//---------------------------------------------------------

bool
Shop::remove_product( const QString &name,
                      const Proveider &proveider,
                      Product::PRICE_TYPE price_type )
{
    bool removed = false;

    for( uint i = 0; i < m_products.size( ); ++i )
    {
        if ( m_products[ i ].name( ) == name
             && m_products[ i ].proveider().name() == proveider.name()
             && m_products[ i ].price_type() == price_type )
        {
            m_products.erase( m_products.begin() + i );
            removed = true;
            break;
        }
    }

    return removed;
}

//---------------------------------------------------------

bool
Shop::remove_invoice( const QString &customer_name,
                      const QDate &date,
                      float total_price,
                      float total_profit )
{
    bool removed = false;

    for( uint i = 0; i < m_invoices.size( ); ++i )
    {
        const Invoice& invoice = m_invoices[ i ];

        if ( invoice.customer( ).name( ) == customer_name
             && invoice.date() == date
             && invoice.total_cost() == total_price
             && invoice.profit() == total_profit )
        {
            m_invoices.erase( m_invoices.begin() + i );
            removed = true;
            break;
        }
    }

    return removed;
}

//---------------------------------------------------------

bool
Shop::update_product( const Product &product )
{
    bool updated = false;

    for( uint i = 0; i < m_products.size( ); ++i )
    {
        if ( m_products[ i ].id( ) == product.id( ) )
        {
            updated = m_products[ i ].reset( product );
            break;
        }
    }

    return updated;
}

//---------------------------------------------------------

bool
Shop::parse_invoices( const QString &file_invoices_csv )
{
    m_invoices.clear( );

    // expects '/' as directory separator regardless of operating system.
    QFile file_invoices( file_invoices_csv );

    bool result = true;

    if ( !file_invoices.open( QFile::ReadOnly ) )
    {
        assert( 0 );
        result = false;
    }

    if( !parse_invoices( file_invoices, ';' ) )
    {
        assert( 0 );
        result = false;
    }

    file_invoices.close( );

    return result;
}

//---------------------------------------------------------

bool
Shop::parse_invoices( QFile& file, const char separator )
{
    QTextStream stream( &file );

    while ( !stream.atEnd( ) )
    {
        QString line = stream.readLine( );

        while( line.startsWith( '#' )
               || line.isEmpty() )
        {
            line = stream.readLine( );

            if ( stream.atEnd() && line.isEmpty() )
            {
                return true;
            }
        }

        std::vector<int> separators =
                CustomerParser::separators_in_line( line, separator );

        // list should contain 9 separators
        //      Invoice id,Customer Id,Product ids,total taxes,total without taxes,total profit,total(total taxes+total without taxes)
        // i.e. 1;2;1,2,3;25,100,125
        if( separators.size() != 6 )
        {
            assert( 0 );
            return false;
        }

        parse_invoices_line( line, separator );
    }

    return true;
}

//---------------------------------------------------------

Customer*
Shop::get_customer( int customer_id )
{
    for( uint i = 0; i < m_customers.size( ); ++i )
    {
        if( m_customers[ i ].id( ) == customer_id )
        {
            return &m_customers[ i ];
        }
    }

    return 0;
}

//---------------------------------------------------------

Customer
Shop::get_customer( const QString& name )
{
    QString name_lower_case = name.toLower( );

    for( uint i = 0; i < m_customers.size( ); ++i )
    {
        if( m_customers[ i ].name( ).toLower() == name_lower_case )
        {
            return m_customers[ i ];
        }
    }

    return Customer( );
}

//---------------------------------------------------------

Product*
Shop::get_product( const QString& name )
{
    QString name_lower_case = name.toLower( );

    while ( name_lower_case.startsWith( ' ') )
    {
        // remove first character if it is a space
        name_lower_case.remove( 0, 1 );
    }

    for( uint i = 0; i < m_products.size( ); ++i )
    {
        if( m_products[ i ].name( ).toLower() == name_lower_case )
        {
            return &m_products[ i ];
        }
    }

    assert( 0 );
    return 0;
}

//---------------------------------------------------------

Product*
Shop::get_product( int id )
{
    for( uint i = 0; i < m_products.size( ); ++i )
    {
        if( m_products[ i ].id() == id )
        {
            return &m_products[ i ];
        }
    }

    assert( 0 );
    return 0;
}

//---------------------------------------------------------

std::vector< Product >
Shop::get_products_with_names( const QStringList &product_list )
{
    std::vector< Product > products;

    for( int i = 0; i < product_list.size( ); ++i )
    {
        const Product* product = get_product( product_list.at( i ) );
        if( !product )
        {
            assert( 0 );
            return products;
        }

        products.push_back( *product );
    }

    return products;
}

//---------------------------------------------------------

bool
Shop::parse_invoices_line( const QString &line, const char separator )
{
    // TODO Id needed??
    //int id = line.section( separator,
    //                       InvoicesModel::INVOICE_ID,
    //                       InvoicesModel::INVOICE_ID ).toInt( );

    QString customer_name = line.section( separator,
                                          InvoicesModel::INVOICE_CUSTOMER_NAME,
                                          InvoicesModel::INVOICE_CUSTOMER_NAME );

    QString date_str = line.section( separator,
                                     InvoicesModel::INVOICE_DATE,
                                     InvoicesModel::INVOICE_DATE );

    QDate date = QDate::fromString( date_str, "dd.MM.yyyy" );

    QString product_names = line.section( separator,
                                          InvoicesModel::INVOICE_PRODUCT_NAMES,
                                          InvoicesModel::INVOICE_PRODUCT_NAMES );

    std::vector< Product > products = get_products_with_names( product_names.split( ',' ) );

    // TODO refactor this
    Customer customer = get_customer( customer_name );

    if ( customer.id() == -1 )
    {
        assert( 0 );
        return false;
    }

    Invoice invoice( date, customer );

    for ( uint i = 0; i < products.size( ); ++i )
    {
        // TODO quantity 1 ?? discount 0 ??
        InvoiceItem item( customer.type( ), m_products[ i ], 1, 0  );
        invoice.add_item( item );
    }

    m_invoices.push_back( invoice );

    // TODO error handling
    return true;
}

//---------------------------------------------------------

std::vector< Invoice >
Shop::invoices( const Customer& customer ) const
{
    std::vector< Invoice > invoices;

    for( size_t i = 0; i < m_invoices.size( ); ++i )
    {
        if ( m_invoices[ i ].customer().name( ) == customer.name( ) )
        {
            invoices.push_back( m_invoices[ i ] );
        }
    }

    return invoices;
}
